home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / hity wydania / Ubuntu 9.10 PL / karmelkowy-koliberek-desktop-9.10-i386-PL.iso / casper / filesystem.squashfs / usr / share / hplip / sendfax.py < prev    next >
Text File  |  2009-10-09  |  21KB  |  573 lines

  1. #!/usr/bin/env python
  2. # -*- coding: utf-8 -*-
  3. #
  4. # (c) Copyright 2003-2009 Hewlett-Packard Development Company, L.P.
  5. #
  6. # This program is free software; you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation; either version 2 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with this program; if not, write to the Free Software
  18. # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  19. #
  20. # Author: Don Welch
  21. #
  22. # Thanks to Henrique M. Holschuh <hmh@debian.org> for various security patches
  23. #
  24.  
  25. __version__ = '9.0'
  26. __title__ = 'PC Sendfax Utility'
  27. __mod__ = 'hp-sendfax'
  28. __doc__ = "PC send fax for HPLIP supported multifunction printers."
  29.  
  30. # Std Lib
  31. import sys
  32. import os
  33. import os.path
  34. import getopt
  35. import signal
  36. import time
  37. import operator
  38.  
  39. # Local
  40. from base.g import *
  41. import base.utils as utils
  42. from base import device, tui, module
  43.  
  44. username = prop.username
  45. faxnum_list = []
  46. recipient_list = []
  47. group_list = []
  48. prettyprint = False
  49.  
  50. mod = module.Module(__mod__, __title__, __version__, __doc__, None,
  51.                     (GUI_MODE, NON_INTERACTIVE_MODE),
  52.                     (UI_TOOLKIT_QT3, UI_TOOLKIT_QT4))
  53.  
  54. mod.setUsage(module.USAGE_FLAG_DEVICE_ARGS | module.USAGE_FLAG_SUPRESS_G_DEBUG_FLAG,
  55.     extra_options=[
  56.     ("Specify the fax number(s):", "-f<number(s)> or --faxnum=<number(s)> or --fax-num=<number(s)>  or --num=<number(s)>(-n only)", "option", False),
  57.     ("Specify the recipient(s):", "-r<recipient(s)> or --recipient=<recipient(s)> (-n only)", "option", False),
  58.     ("Specify the groups(s):", "--group=<group(s)> or --groups=<group(s)> (-n only)", "option", False) ],
  59.     see_also_list=['hp-faxsetup', 'hp-fab'])
  60.  
  61. opts, device_uri, printer_name, mode, ui_toolkit, loc = \
  62.     mod.parseStdOpts('f:r:g:',
  63.                      ['faxnum=', 'fax-num=', 'recipient=', 'group=',
  64.                       'groups=', 'gg'],
  65.                       supress_g_debug_flag=True)
  66.  
  67. for o, a in opts:
  68.     if o == '--gg':
  69.         log.set_level('debug')
  70.  
  71.     elif o in ('-z', '--logfile'):
  72.         log.set_logfile(a)
  73.         log.set_where(log.LOG_TO_CONSOLE_AND_FILE)
  74.  
  75.     elif o == '--fax':
  76.         printer_name = a
  77.  
  78.     elif o in ('-f', '--faxnum', '--fax-num', '--num'):
  79.         faxnum_list.extend(a.split(','))
  80.  
  81.     elif o in ('-r', '--recipient'):
  82.         recipient_list.extend(a.split(','))
  83.  
  84.     elif o in ('-g', '--group'):
  85.         group_list.extend(a.split(','))
  86.  
  87.  
  88. if not prop.fax_build:
  89.     log.error("Fax is disabled (turned off during build). Exiting")
  90.     sys.exit(1)
  91.  
  92. printer_name, device_uri = mod.getPrinterName(printer_name, device_uri,
  93.     filter={'fax-type': (operator.gt, 0)}, back_end_filter=['hpfax'])
  94.  
  95. if mode == GUI_MODE:
  96.     if ui_toolkit == 'qt3':
  97.         if not utils.canEnterGUIMode():
  98.             log.error("%s requires GUI support (try running with --qt4). Also, try using non-interactive (-n) mode." % __mod__)
  99.             sys.exit(1)
  100.     else:
  101.         if not utils.canEnterGUIMode4():
  102.             log.error("%s requires GUI support (try running with --qt3). Also, try using non-interactive (-n) mode." % __mod__)
  103.             sys.exit(1)
  104.  
  105. if mode == GUI_MODE:
  106.     if ui_toolkit == 'qt3':
  107.         app = None
  108.         sendfax = None
  109.  
  110.         try:
  111.             from qt import *
  112.             from ui.faxsendjobform import FaxSendJobForm
  113.         except ImportError:
  114.             log.error("Unable to load Qt3 support. Is it installed?")
  115.             sys.exit(1)
  116.  
  117.         # create the main application object
  118.         app = QApplication(sys.argv)
  119.  
  120.         if loc is None:
  121.             loc = user_conf.get('ui', 'loc', 'system')
  122.             if loc.lower() == 'system':
  123.                 loc = str(QTextCodec.locale())
  124.                 log.debug("Using system locale: %s" % loc)
  125.  
  126.         if loc.lower() != 'c':
  127.             e = 'utf8'
  128.             try:
  129.                 l, x = loc.split('.')
  130.                 loc = '.'.join([l, e])
  131.             except ValueError:
  132.                 l = loc
  133.                 loc = '.'.join([loc, e])
  134.  
  135.             log.debug("Trying to load .qm file for %s locale." % loc)
  136.             trans = QTranslator(None)
  137.  
  138.             qm_file = 'hplip_%s.qm' % l
  139.             log.debug("Name of .qm file: %s" % qm_file)
  140.             loaded = trans.load(qm_file, prop.localization_dir)
  141.  
  142.             if loaded:
  143.                 app.installTranslator(trans)
  144.             else:
  145.                 loc = 'c'
  146.  
  147.         if loc == 'c':
  148.             log.debug("Using default 'C' locale")
  149.         else:
  150.             log.debug("Using locale: %s" % loc)
  151.  
  152.             QLocale.setDefault(QLocale(loc))
  153.             prop.locale = loc
  154.             try:
  155.                 locale.setlocale(locale.LC_ALL, locale.normalize(loc))
  156.             except locale.Error:
  157.                 pass
  158.  
  159.  
  160.         if os.geteuid() == 0:
  161.             log.error("You must not be root to run this utility.")
  162.  
  163.             QMessageBox.critical(None,
  164.                                  "HP Device Manager - Send Fax",
  165.                                  "You must not be root to run hp-sendfax.",
  166.                                   QMessageBox.Ok,
  167.                                   QMessageBox.NoButton,
  168.                                   QMessageBox.NoButton)
  169.  
  170.             sys.exit(1)
  171.  
  172.         # TODO: Fix instance lock
  173.         sendfax = FaxSendJobForm(device_uri,
  174.                                  printer_name,
  175.                                  mod.args)
  176.  
  177.         app.setMainWidget(sendfax)
  178.  
  179.         pid = os.getpid()
  180.         log.debug('pid=%d' % pid)
  181.  
  182.         sendfax.show()
  183.  
  184.         try:
  185.             log.debug("Starting GUI loop...")
  186.             app.exec_loop()
  187.         except KeyboardInterrupt:
  188.             pass
  189.  
  190.     else: # qt4
  191.         #try:
  192.         if 1:
  193.             from PyQt4.QtGui import QApplication
  194.             from ui4.sendfaxdialog import SendFaxDialog
  195.         #except ImportError:
  196.         if 0:
  197.             log.error("Unable to load Qt4 support. Is it installed?")
  198.             sys.exit(1)
  199.  
  200.         app = QApplication(sys.argv)
  201.  
  202.         dlg = SendFaxDialog(None, printer_name, device_uri, mod.args)
  203.         dlg.show()
  204.  
  205.         try:
  206.             log.debug("Starting GUI loop...")
  207.             app.exec_()
  208.         except KeyboardInterrupt:
  209.             sys.exit(0)
  210.  
  211.  
  212.  
  213.  
  214. else: # NON_INTERACTIVE_MODE
  215.     if os.getuid() == 0:
  216.         log.error("%s cannot be run as root." % __mod__)
  217.         sys.exit(1)
  218.  
  219.     try:
  220.         import struct, Queue
  221.         from prnt import cups
  222.         from base import magic
  223.  
  224.         try:
  225.             from fax import fax
  226.         except ImportError:
  227.             # This can fail on Python < 2.3 due to the datetime module
  228.             log.error("Fax address book disabled - Python 2.3+ required.")
  229.             sys.exit(1)
  230.  
  231.         db =  fax.FaxAddressBook() # FAB instance
  232.  
  233.         try:
  234.             import dbus
  235.         except ImportError:
  236.             log.error("PC send fax requires dBus and python-dbus")
  237.             sys.exit(1)
  238.  
  239.         import warnings
  240.         # Ignore: .../dbus/connection.py:242: DeprecationWarning: object.__init__() takes no parameters
  241.         # (occurring on Python 2.6/dBus 0.83/Ubuntu 9.04)
  242.         warnings.simplefilter("ignore", DeprecationWarning)
  243.  
  244.         dbus_avail, service, session_bus = device.init_dbus()
  245.  
  246.         if not dbus_avail or service is None:
  247.             log.error("Unable to initialize dBus. PC send fax requires dBus and hp-systray support. Exiting.")
  248.             sys.exit(1)
  249.  
  250.         phone_num_list = []
  251.  
  252.         log.debug("Faxnum list = %s" % faxnum_list)
  253.         faxnum_list = utils.uniqueList(faxnum_list)
  254.         log.debug("Unique list=%s" % faxnum_list)
  255.  
  256.         for f in faxnum_list:
  257.             for c in f:
  258.                 if c not in '0123456789-(+) *#':
  259.                     log.error("Invalid character in fax number '%s'. Only the characters '0123456789-(+) *#' are valid." % f)
  260.                     sys.exit(1)
  261.  
  262.         log.debug("Group list = %s" % group_list)
  263.         group_list = utils.uniqueList(group_list)
  264.         log.debug("Unique list=%s" % group_list)
  265.  
  266.         for g in group_list:
  267.             entries = db.group_members(g)
  268.             if not entries:
  269.                 log.warn("Unknown group name: %s" % g)
  270.             else:
  271.                 for e in entries:
  272.                     recipient_list.append(e)
  273.  
  274.         log.debug("Recipient list = %s" % recipient_list)
  275.         recipient_list = utils.uniqueList(recipient_list)
  276.         log.debug("Unique list=%s" % recipient_list)
  277.  
  278.         for r in recipient_list:
  279.             if db.get(r) is None:
  280.                 log.error("Unknown fax recipient '%s' in the recipient list." % r)
  281.                 all_entries = db.get_all_records()
  282.                 log.info(log.bold("\nKnown recipients (entries):"))
  283.  
  284.                 for a in all_entries:
  285.                     aa = db.get(a)
  286.                     log.info("%s (fax number: %s)" % (a, aa['fax']))
  287.  
  288.                 print
  289.                 sys.exit(1)
  290.  
  291.         for p in recipient_list:
  292.             a = db.get(p)
  293.             if a['fax']:
  294.                 phone_num_list.append(a)
  295.                 log.debug("Name=%s Number=%s" % (a['name'], a['fax']))
  296.  
  297.         for p in faxnum_list:
  298.             phone_num_list.append({'fax': p, 'name': u'Unknown'})
  299.             log.debug("Number=%s" % p)
  300.  
  301.         log.debug("Phone num list = %s" % phone_num_list)
  302.  
  303.         if not phone_num_list:
  304.             mod.usage(error_msg=["No recipients specified. Please use -f, -r, and/or -g to specify recipients."])
  305.  
  306.         allowable_mime_types = cups.getAllowableMIMETypes()
  307.  
  308.         for f in mod.args:
  309.             path = os.path.realpath(f)
  310.             log.debug(path)
  311.  
  312.             if os.path.exists(path):
  313.                 mime_type = magic.mime_type(path)
  314.                 log.debug(mime_type)
  315.             else:
  316.                 log.error("File '%s' does not exist." % path)
  317.                 sys.exit(1)
  318.  
  319.             if mime_type not in allowable_mime_types:
  320.                 log.error("File '%s' has a non-allowed mime-type of '%s'" % (path, mime_type))
  321.                 sys.exit(1)
  322.  
  323.         log.info(log.bold("Using fax %s (%s)" % (printer_name, device_uri)))
  324.  
  325.         #ok, lock_file = utils.lock_app('%s-%s' % (__mod__, printer_name), True)
  326.         mod.lockInstance(printer_name)
  327.  
  328.         try:
  329.             ppd_file = cups.getPPD(printer_name)
  330.  
  331.             if ppd_file is not None and os.path.exists(ppd_file):
  332.                 if file(ppd_file, 'r').read(8192).find('HP Fax') == -1:
  333.                     log.error("Fax configuration error. The CUPS fax queue for '%s' is incorrectly configured. Please make sure that the CUPS fax queue is configured with the 'HP Fax' Model/Driver." % printer_name)
  334.                     sys.exit(1)
  335.  
  336.             if not mod.args:
  337.                 mod.usage(error_msg=["No files specfied to send. Please specify the file(s) to send on the command line."])
  338.  
  339.             file_list = []
  340.  
  341.             for f in mod.args:
  342.  
  343.                 #
  344.                 # Submit each file to CUPS for rendering by hpijsfax
  345.                 #
  346.                 path = os.path.realpath(f)
  347.                 log.debug(path)
  348.                 mime_type = magic.mime_type(path)
  349.  
  350.                 if mime_type == 'application/hplip-fax': # .g3
  351.                     log.info("\nPreparing fax file %s..." % f)
  352.                     fax_file_fd = file(f, 'r')
  353.                     header = fax_file_fd.read(fax.FILE_HEADER_SIZE)
  354.                     fax_file_fd.close()
  355.  
  356.                     mg, version, pages, hort_dpi, vert_dpi, page_size, \
  357.                         resolution, encoding, reserved1, reserved2 = struct.unpack(">8sBIHHBBBII", header)
  358.  
  359.                     if mg != 'hplip_g3':
  360.                         log.error("%s: Invalid file header. Bad magic." % f)
  361.                         sys.exit(1)
  362.  
  363.                     file_list.append((f, mime_type, "", "", pages))
  364.  
  365.                 else:
  366.                     all_pages = True
  367.                     page_range = ''
  368.                     page_set = 0
  369.                     nup = 1
  370.  
  371.                     cups.resetOptions()
  372.  
  373.                     if mime_type in ["application/x-cshell",
  374.                                      "application/x-perl",
  375.                                      "application/x-python",
  376.                                      "application/x-shell",
  377.                                      "text/plain",] and prettyprint:
  378.  
  379.                         cups.addOption('prettyprint')
  380.  
  381.                     if nup > 1:
  382.                         cups.addOption('number-up=%d' % nup)
  383.  
  384.                     while True:
  385.  
  386.                         cups_printers = cups.getPrinters()
  387.                         printer_state = cups.IPP_PRINTER_STATE_STOPPED
  388.                         for p in cups_printers:
  389.                             if p.name == printer_name:
  390.                                 printer_state = p.state
  391.  
  392.                         log.debug("Printer state = %d" % printer_state)
  393.  
  394.                         if printer_state == cups.IPP_PRINTER_STATE_IDLE:
  395.                             log.debug("Printer name = %s file = %s" % (printer_name, path))
  396.                             sent_job_id = cups.printFile(printer_name, path, os.path.basename(path))
  397.                             log.info("\nRendering file '%s' (job %d)..." % (path, sent_job_id))
  398.                             log.debug("Job ID=%d" % sent_job_id)
  399.                             break
  400.                         elif printer_state == cups.IPP_PRINTER_STATE_PROCESSING:
  401.                             log.debug("Waiting for CUPS queue '%s' to become idle." % printer_name)
  402.                         else:
  403.                             log.error("The CUPS queue for '%s' is in a stopped or busy state (%d). Please check the queue and try again." % (printer_name, printer_state))
  404.                             sys.exit(1)
  405.  
  406.                     cups.resetOptions()
  407.  
  408.                     #
  409.                     # Wait for fax to finish rendering
  410.                     #
  411.  
  412.                     end_time = time.time() + 120.0
  413.                     while time.time() < end_time:
  414.                         log.debug("Waiting for fax...")
  415.                         try:
  416.                             result = list(service.CheckForWaitingFax(device_uri, prop.username, sent_job_id))
  417.                             log.debug(repr(result))
  418.  
  419.                         except dbus.exceptions.DBusException:
  420.                             log.error("Cannot communicate with hp-systray. Canceling...")
  421.                             cups.cancelJob(sent_job_id)
  422.                             sys.exit(1)
  423.  
  424.                         fax_file = str(result[7])
  425.                         log.info(fax_file)
  426.  
  427.                         if fax_file:
  428.                             log.debug("Fax file=%s" % fax_file)
  429.                             title = str(result[5])
  430.                             break
  431.  
  432.                         time.sleep(1)
  433.  
  434.                     else:
  435.                         log.error("Timeout waiting for rendering. Canceling job #%d..." % sent_job_id)
  436.                         cups.cancelJob(sent_job_id)
  437.                         sys.exit(1)
  438.  
  439.                     # open the rendered file to read the file header
  440.                     f = file(fax_file, 'r')
  441.                     header = f.read(fax.FILE_HEADER_SIZE)
  442.  
  443.                     if len(header) != fax.FILE_HEADER_SIZE:
  444.                         log.error("Invalid fax file! (truncated header or no data)")
  445.                         sys.exit(1)
  446.  
  447.                     mg, version, total_pages, hort_dpi, vert_dpi, page_size, \
  448.                         resolution, encoding, reserved1, reserved2 = \
  449.                         struct.unpack(">8sBIHHBBBII", header[:fax.FILE_HEADER_SIZE])
  450.  
  451.                     log.debug("Magic=%s Ver=%d Pages=%d hDPI=%d vDPI=%d Size=%d Res=%d Enc=%d" %
  452.                               (mg, version, total_pages, hort_dpi, vert_dpi, page_size, resolution, encoding))
  453.  
  454.                     file_list.append((fax_file, mime_type, "", title, total_pages))
  455.                     f.close()
  456.  
  457.             #
  458.             # Insure that the device is in an OK state
  459.             #
  460.  
  461.             dev = None
  462.  
  463.             log.debug("\nChecking device state...")
  464.             try:
  465.                 dev = fax.getFaxDevice(device_uri, printer_name)
  466.  
  467.                 try:
  468.                     dev.open()
  469.                 except Error, e:
  470.                     log.warn(e.msg)
  471.  
  472.                 try:
  473.                     dev.queryDevice(quick=True)
  474.                 except Error, e:
  475.                     log.error("Query device error (%s)." % e.msg)
  476.                     dev.error_state = ERROR_STATE_ERROR
  477.  
  478.                 if dev.error_state > ERROR_STATE_MAX_OK and \
  479.                     dev.error_state not in (ERROR_STATE_LOW_SUPPLIES, ERROR_STATE_LOW_PAPER):
  480.  
  481.                     log.error("Device is busy or in an error state (code=%d). Please wait for the device to become idle or clear the error and try again." % dev.error_state)
  482.                     sys.exit(1)
  483.  
  484.                 user_conf.set('last_used', 'device_uri', dev.device_uri)
  485.  
  486.                 log.debug("File list:")
  487.  
  488.                 for f in file_list:
  489.                     log.debug(str(f))
  490.  
  491.                 service.SendEvent(device_uri, printer_name, EVENT_START_FAX_JOB, prop.username, 0, '')
  492.  
  493.                 update_queue = Queue.Queue()
  494.                 event_queue = Queue.Queue()
  495.  
  496.                 log.info("\nSending fax...")
  497.  
  498.                 if not dev.sendFaxes(phone_num_list, file_list, "",
  499.                                      "", None, False, printer_name,
  500.                                      update_queue, event_queue):
  501.  
  502.                     log.error("Send fax is active. Please wait for operation to complete.")
  503.                     service.SendEvent(device_uri, printer_name, EVENT_FAX_JOB_FAIL, prop.username, 0, '')
  504.                     sys.exit(1)
  505.  
  506.                 try:
  507.                     cont = True
  508.                     while cont:
  509.                         while update_queue.qsize():
  510.                             try:
  511.                                 status, page_num, phone_num = update_queue.get(0)
  512.                             except Queue.Empty:
  513.                                 break
  514.  
  515.                             if status == fax.STATUS_IDLE:
  516.                                 log.debug("Idle")
  517.  
  518.                             elif status == fax.STATUS_PROCESSING_FILES:
  519.                                 log.info("\nProcessing page %d" % page_num)
  520.  
  521.                             elif status == fax.STATUS_DIALING:
  522.                                 log.info("\nDialing %s..." % phone_num)
  523.  
  524.                             elif status == fax.STATUS_CONNECTING:
  525.                                 log.info("\nConnecting to %s..." % phone_num)
  526.  
  527.                             elif status == fax.STATUS_SENDING:
  528.                                 log.info("\nSending page %d to %s..." % (page_num, phone_num))
  529.  
  530.                             elif status == fax.STATUS_CLEANUP:
  531.                                 log.info("\nCleaning up...")
  532.  
  533.                             elif status in (fax.STATUS_ERROR, fax.STATUS_BUSY, fax.STATUS_COMPLETED):
  534.                                 cont = False
  535.  
  536.                                 if status  == fax.STATUS_ERROR:
  537.                                     log.error("Fax send error.")
  538.                                     service.SendEvent(device_uri, printer_name, EVENT_FAX_JOB_FAIL, prop.username, 0, '')
  539.  
  540.                                 elif status == fax.STATUS_BUSY:
  541.                                     log.error("Fax device is busy. Please try again later.")
  542.                                     service.SendEvent(device_uri, printer_name, EVENT_FAX_JOB_FAIL, prop.username, 0, '')
  543.  
  544.                                 elif status == fax.STATUS_COMPLETED:
  545.                                     log.info("\nCompleted successfully.")
  546.                                     service.SendEvent(device_uri, printer_name, EVENT_END_FAX_JOB, prop.username, 0, '')
  547.  
  548.                         update_spinner()
  549.                         time.sleep(2)
  550.  
  551.                     cleanup_spinner()
  552.  
  553.                 except KeyboardInterrupt:
  554.                     event_queue.put((fax.EVENT_FAX_SEND_CANCELED, '', '', ''))
  555.                     service.SendEvent(device_uri, printer_name, EVENT_FAX_JOB_CANCELED, prop.username, 0, '')
  556.                     log.error("Cancelling...")
  557.  
  558.             finally:
  559.                 log.debug("Waiting for send fax thread to exit...")
  560.                 if dev is not None:
  561.                     dev.waitForSendFaxThread()
  562.                     log.debug("Closing device...")
  563.                     dev.close()
  564.  
  565.         finally:
  566.             mod.unlockInstance()
  567.  
  568.     except KeyboardInterrupt:
  569.         log.error("User exit")
  570.  
  571. log.info("")
  572. log.info("Done.")
  573.